﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Linq;
using System.ComponentModel;
using System.Collections.Specialized;

namespace Gmantis.Controls.DataGrid
{
    public class DataGridComboBoxColumn : DataGridBoundColumn
    {
        // Fields
        public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(DataGridComboBoxColumn), new PropertyMetadata(new PropertyChangedCallback(DataGridComboBoxColumn.OnItemsSourcePropertyChanged)));

        // Methods
        public DataGridComboBoxColumn()
        {
            this.Initialize();
        }

        public DataGridComboBoxColumn(PropertyInfo property)
            : base(property)
        {
            this.Initialize();
            if (property.PropertyType.GetNonNullableType().IsEnum)
            {
                List<object> list = new List<object>();
                foreach (FieldInfo info in property.PropertyType.GetNonNullableType().GetFields())
                {
                    if (!info.IsSpecialName)
                    {
                        list.Add(Enum.Parse(property.PropertyType.GetNonNullableType(), info.Name, false));
                    }
                }
                this.ItemsSource = list;
            }
        }

        public override bool BeginEdit(FrameworkElement editingElement, RoutedEventArgs routedEventArgs)
        {
            if (!(routedEventArgs is System.Windows.Input.KeyEventArgs))
            {
                return true;
            }
            string keyString = KeyboardUtil.GetKeyString((System.Windows.Input.KeyEventArgs)routedEventArgs);
            if (keyString != null)
            {
                ((ComboBox)editingElement).BeginEdit(keyString);
                return true;
            }
            return false;
        }

        public override void CancelCellEdit(FrameworkElement editingElement, object uneditedValue)
        {
            ComboBox box = (ComboBox)editingElement;
            box.IsDropDownOpen = false;
            if (!string.IsNullOrEmpty(this.SelectedValuePath))
            {
                box.SelectedValue = uneditedValue;
            }
            else
            {
                box.SelectedItem = uneditedValue;
            }
        }

        public override void EndEdit(FrameworkElement editingElement)
        {
            ComboBox box = editingElement as ComboBox;
            if (box != null)
            {
                if (box._elementComboHeader != null)
                {
                    box._elementComboHeader.IsEditable = false;
                }
                BindingExpression bindingExpression = box.GetBindingExpression(string.IsNullOrEmpty(this.SelectedValuePath) ? ComboBox.SelectedItemProperty : ComboBox.SelectedValueProperty);
                if (bindingExpression != null)
                {
                    bindingExpression.UpdateSource();
                }
            }
        }

        public override FrameworkElement GetCellEditingContent(DataGridRow row)
        {
            ComboBox box = new ComboBox
            {
                IsEditable = false,
                Padding = new Thickness(0.0),
                CornerRadius = new CornerRadius(0.0)
            };
            if (this.ItemTemplate != null)
            {
                box.ItemTemplate = this.ItemTemplate;
            }
            else
            {
                box.DisplayMemberPath = this.DisplayMemberPath;
            }
            if (this.ItemConverter != null)
            {
                box.ItemConverter = this.ItemConverter;
            }
            if (this.ItemsSource != null)
            {
                box.ItemsSource = this.ItemsSource.Cast<object>().ToList<object>();
            }
            box.DropDownWidth = this.DropDownWidth;
            box.DropDownHeight = this.DropDownHeight;
            box.MaxDropDownWidth = this.MaxDropDownWidth;
            box.MaxDropDownHeight = this.MaxDropDownHeight;
            if (!string.IsNullOrEmpty(this.SelectedValuePath))
            {
                box.SelectedValuePath = this.SelectedValuePath;
                if (base.Binding != null)
                {
                    box.SetBinding(ComboBox.SelectedValueProperty, base.CopyBinding(base.Binding, null));
                }
            }
            else if (base.Binding != null)
            {
                box.SetBinding(ComboBox.SelectedItemProperty, base.CopyBinding(base.Binding, null));
            }
            box.TextAlignment = (base.HorizontalAlignment == HorizontalAlignment.Left) ? TextAlignment.Left : ((base.HorizontalAlignment == HorizontalAlignment.Right) ? TextAlignment.Right : TextAlignment.Center);
            box.HorizontalContentAlignment = base.HorizontalAlignment;
            box.VerticalContentAlignment = base.VerticalAlignment;
            if (base.CellEditingContentStyle != null)
            {
                box.Style = base.CellEditingContentStyle;
            }
            return box;
        }

        public override IDataGridFilter GetFilter()
        {
            DataGridFilter filter = new DataGridFilter();
            DataGridMultiValueFilter target = new DataGridMultiValueFilter();
            target.SetBinding(ItemsControl.DisplayMemberPathProperty, new Binding().From<DataGridComboBoxColumn>(this, x => x.DisplayMemberPath));
            target.SetBinding(DataGridMultiValueFilter.ValueMemberPathProperty, new Binding().From<DataGridComboBoxColumn>(this, x => x.SelectedValuePath));
            target.SetBinding(ItemsControl.ItemsSourceProperty, new Binding().From<DataGridComboBoxColumn>(this, x => x.ItemsSource));
            target.SetBinding<DataGrid>(Control.BackgroundProperty, base.DataGrid, dg => dg.HeaderBackground);
            target.SetBinding<DataGrid>(Control.ForegroundProperty, base.DataGrid, dg => dg.HeaderForeground);
            target.SetBinding<DataGrid>(Control.BorderBrushProperty, base.DataGrid, dg => dg.BorderBrush);
            target.SetBinding<DataGrid>(DataGridMultiValueFilter.MouseOverBrushProperty, base.DataGrid, dg => dg.MouseOverBrush);
            target.SetBinding<DataGrid>(DataGridMultiValueFilter.PressedBrushProperty, base.DataGrid, dg => dg.PressedBrush);
            target.SetBinding<DataGrid>(DataGridMultiValueFilter.FocusBrushProperty, base.DataGrid, dg => dg.SelectedBackground);
            target.SetBinding<DataGrid>(DataGridMultiValueFilter.InputBackgroundProperty, base.DataGrid, dg => dg.RowBackground);
            target.SetBinding<DataGrid>(DataGridMultiValueFilter.InputForegroundProperty, base.DataGrid, dg => dg.RowForeground);
            filter.InnerControl = target;
            return filter;
        }

        private void Initialize()
        {
            this.DropDownWidth = double.NaN;
            this.DropDownHeight = double.NaN;
            this.MaxDropDownWidth = double.PositiveInfinity;
            this.MaxDropDownHeight = 200.0;
        }

        private void InitializeItemsSource(IEnumerable iEnumerable)
        {
            if (iEnumerable is INotifyCollectionChanged)
            {
                ((INotifyCollectionChanged)iEnumerable).CollectionChanged += new NotifyCollectionChangedEventHandler(this.ItemsSource_CollectionChanged);
            }
        }

        private void ItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            base.Refresh(true, false, true);
        }

        private static void OnItemsSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (e.OldValue != null)
            {
                (d as DataGridComboBoxColumn).ReleaseItemsSource((IEnumerable)e.OldValue);
            }
            if (e.NewValue != null)
            {
                (d as DataGridComboBoxColumn).InitializeItemsSource((IEnumerable)e.NewValue);
            }
            (d as DataGridComboBoxColumn).Refresh();
        }

        public override object PrepareCellForEdit(FrameworkElement editingElement)
        {
            if (!string.IsNullOrEmpty(this.SelectedValuePath))
            {
                return (editingElement as ComboBox).SelectedValue;
            }
            return (editingElement as ComboBox).SelectedItem;
        }

        private void ReleaseItemsSource(IEnumerable iEnumerable)
        {
            if (iEnumerable is INotifyCollectionChanged)
            {
                ((INotifyCollectionChanged)iEnumerable).CollectionChanged -= new NotifyCollectionChangedEventHandler(this.ItemsSource_CollectionChanged);
            }
        }

        // Properties
        public override IValueConverter DefaultConverter
        {
            get
            {
                return new ComboDefaultConverter(this) { DefaultValue = this.ItemNotFoundText };
            }
        }

        public string DisplayMemberPath { get; set; }

        public double DropDownHeight { get; set; }

        public double DropDownWidth { get; set; }

        public override bool IsEditable
        {
            get
            {
                return (base.Binding != null);
            }
        }

        public TypeConverter ItemConverter { get; set; }

        public string ItemNotFoundText { get; set; }

        public IEnumerable ItemsSource
        {
            get
            {
                return (IEnumerable)base.GetValue(ItemsSourceProperty);
            }
            set
            {
                base.SetValue(ItemsSourceProperty, value);
            }
        }

        public DataTemplate ItemTemplate { get; set; }

        public double MaxDropDownHeight { get; set; }

        public double MaxDropDownWidth { get; set; }

        public string SelectedValuePath { get; set; }
    }
}
